package controllers;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import libs.Constants;
import libs.Validation;
import models.Conversation;
import models.Group;
import models.User;
import models.VisitObject;
import models.forms.LoginForm;
import models.forms.SignupForm;

import org.bson.types.ObjectId;

import play.data.Form;
import play.data.validation.ValidationError;
import play.libs.Json;
import play.mvc.Controller;
import play.mvc.Result;
import views.html.index;
import views.html.user.login;
import views.html.user.signup;
import business.ConversationBusiness;
import business.GroupBusiness;
import business.UserBusiness;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Lists;

public class Application extends Controller {

	public static Result index() {
		UserBusiness userBusiness = new UserBusiness();
		List<User> userList = userBusiness.selectAll();
		GroupBusiness groupBusiness = new GroupBusiness();
		String userId = session("userId");
		List<Group> groupList = groupBusiness.selectByUserId(userId);
		
		// Get the last-visit group or user
		// If last-visit is null, the first group will be selected to last-visit
		User userLogin = userBusiness.selectById(session("userId"));
		VisitObject visitObject = userLogin.getLastVisit();
		String visitName = "";
		if(visitObject == null){
			Group firstGroup;
			if(groupList != null && groupList.size() > 0){
				firstGroup = groupList.get(0);
				visitObject = new VisitObject(new ObjectId(firstGroup.getId()), Constants.DEST_GROUP);
			}else{
				firstGroup = groupBusiness.selectAll().get(0);
				visitObject = new VisitObject(new ObjectId(firstGroup.getId()), Constants.DEST_GROUP);
			}
			visitName = firstGroup.getName();
		}else{
			if (Constants.DEST_USER.equals(visitObject.getVisitType())) {
				User lastVisitUser = userBusiness.selectById(visitObject.getVisitId().toString());
				if (lastVisitUser != null) {
					visitName = lastVisitUser.getFullName();
				}
			} else {
				Group lastVisitGroup = groupBusiness.selectById(visitObject.getVisitId().toString());
				if (lastVisitGroup != null) {
					visitName = lastVisitGroup.getName();
				}
			}
		}
		
		// Get limit 100 conversation belong to last-visit
		String lastVisitId = visitObject.getVisitId().toString();
		List<String> routingKeys = new ArrayList<String>();
		ConversationBusiness conversationBusiness = new ConversationBusiness();
		if (Constants.DEST_USER.equals(visitObject.getVisitType())) {
			routingKeys = Lists.newArrayList(Constants.ROUTING_KEY + userId + "-" + lastVisitId, Constants.ROUTING_KEY + lastVisitId + "-" + userId);
		}else{
			routingKeys = Lists.newArrayList(Constants.ROUTING_KEY + lastVisitId);
		}
		List<Conversation> convs = conversationBusiness.selectInByRoutingKey(routingKeys);
		Map<String, Map<String, Conversation>> map = new HashMap<String, Map<String, Conversation>>();
		String previousUserId = "";
		Map<String, Conversation> subMap = new HashMap<String, Conversation>();
		String datetimeKey = "";
		for(Conversation conv: convs){
			if(!previousUserId.equals(conv.getOwnerId().toString())){
				previousUserId = conv.getOwnerId().toString();
				datetimeKey = String.valueOf(conv.getDatetime())+"-"+previousUserId;
				subMap = new HashMap<String, Conversation>();
				subMap.put(String.valueOf(conv.getDatetime()), conv);
				map.put(datetimeKey, subMap);
			}else{
				subMap.put(String.valueOf(conv.getDatetime()), conv);
			}
		}
		return ok(index.render(userList, groupList, map, visitObject, visitName, "Your new application is ready."));
		
	}

	public static Result login() {
		Form<LoginForm> loginForm = Form.form(LoginForm.class);
		if (Validation.isPost()) {
			Form<LoginForm> filledForm = loginForm.bindFromRequest();
			if (filledForm.hasErrors()) {
				return badRequest(login.render(loginForm));
			} else {
				LoginForm form = filledForm.get();
				UserBusiness userBusiness = new UserBusiness();
				User user = userBusiness.login(form);
				if(user != null){
					session("userId", user.getId());
					session("userName", user.getUsername());
					session("userFullName", user.getFullName());
					return redirect(routes.Application.index());
				}else{
					filledForm.reject("username", "Login failed! Your username or password is invalid!");
				}
			}
		}
		return ok(login.render(loginForm));
	}

	public static Result signup() {
		Form<SignupForm> signupForm = Form.form(SignupForm.class);
		if (Validation.isPost()) {
			Form<SignupForm> filledForm = signupForm.bindFromRequest();
			try {
				// Check accept conditions
				/*if (!"true".equals(filledForm.field("accept").value())) {
					filledForm.reject("accept", "You must accept the terms and conditions");
				}*/

				// Check username conditions
				UserBusiness userBusiness = new UserBusiness();
				if (!filledForm.field("username").valueOr("").isEmpty()) {
					if (userBusiness.checkExistByUsername(filledForm.field("username").value())) {
						filledForm
								.reject("username", "Your username is exists on the system. Please choose other one!");
					}
				}

				// Check repeated password
				if (!filledForm.field("password").valueOr("").isEmpty()) {
					if (!filledForm.field("password").valueOr("").equals(filledForm.field("repeatPassword").value())) {
						filledForm.reject("repeatPassword", "Password don't match");
					}
				}

				if (filledForm.hasErrors()) {
					for (String keyError : filledForm.errors().keySet()) {
						List<ValidationError> lstErrors = filledForm.errors().get(keyError);
						for (ValidationError error : lstErrors) {
							System.out.println("Error " + keyError + ": " + error.message().toString());
						}
					}
					return badRequest(signup.render(signupForm));
				} else {
					SignupForm form = filledForm.get();
					userBusiness.save(form);
				}
			} catch (Exception e) {
				System.out.println("Not binding request!--------------");
			}
		}
		return ok(signup.render(signupForm));
	}

	public static Result logout(){
		session().clear();
		return redirect(routes.Application.login());
	}
	
	public static Result chatWith(){
		System.out.println("ChatWith start......");
		JsonNode json = request().body().asJson();
		System.out.println("Json : "+ json.toString());
		String destId = json.findPath("destId").textValue();
		String destType = json.findPath("destType").textValue();
		if (destId != null && destType != null) {
			String userId = session("userId");
			// Update last-visit of user login
			UserBusiness userBusiness = new UserBusiness();
			User userLogin = userBusiness.selectById(userId);
			userLogin.setLastVisit(new VisitObject(new ObjectId(destId), destType));
			userBusiness.updateUser(userLogin);
			
			// Get limit 100 conversation belong to destinationId
			List<String> routingKeys = new ArrayList<String>();
			ConversationBusiness conversationBusiness = new ConversationBusiness();
			if (Constants.DEST_USER.equals(destType)) {
				routingKeys = Lists.newArrayList(Constants.ROUTING_KEY + userId + "-" + destId, Constants.ROUTING_KEY + destId + "-" + userId);
			}else{
				routingKeys = Lists.newArrayList(Constants.ROUTING_KEY + destId);
			}
			List<Conversation> convs = conversationBusiness.selectInByRoutingKey(routingKeys);
			Map<String, Map<String, Conversation>> map = new HashMap<String, Map<String, Conversation>>();
			String previousUserId = "";
			Map<String, Conversation> subMap = new HashMap<String, Conversation>();
			String datetimeKey = "";
			for(Conversation conv: convs){
				if(!previousUserId.equals(conv.getOwnerId().toString())){
					previousUserId = conv.getOwnerId().toString();
					datetimeKey = String.valueOf(conv.getDatetime())+"-"+previousUserId;
					subMap = new HashMap<String, Conversation>();
					subMap.put(String.valueOf(conv.getDatetime()), conv);
					map.put(datetimeKey, subMap);
				}else{
					subMap.put(String.valueOf(conv.getDatetime()), conv);
				}
			}
			String html = views.html.common.line.render(map).body();
			
			ObjectNode result = Json.newObject();
			result.put("status", "OK");
			result.put("message", html);
			return ok(result);
		}else{
			ObjectNode result = Json.newObject();
			result.put("status", "KO");
			result.put("message", "Missing parameters!");
			return ok(result);
		}
	}
}

class ConversationComparator implements Comparator<Long> {

    Map<Long, Conversation> base;
    public ConversationComparator(Map<Long, Conversation> base) {
        this.base = base;
    }

    // Note: this comparator imposes orderings that are inconsistent with equals.    
    public int compare(Long a, Long b) {
        if (base.get(a).getDatetime() >= base.get(b).getDatetime()) {
            return -1;
        } else {
            return 1;
        }
    }
}
